package com.base.library.base

import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.view.*
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.ViewTreeLifecycleOwner
import com.base.library.BR
import com.base.library.R
import com.base.library.ext.listenTo
import com.base.library.ext.showToast
import com.base.library.utils.KeyboardUtils
import com.base.library.utils.ShareViewModelManager
import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType

abstract class BaseVMDialogFragment<VM : BaseVM, VB : ViewDataBinding> :
    DialogFragment() {
    protected lateinit var mVm: VM
    protected lateinit var mVb: VB
    private var mLoadingDialog: LoadingDialog? = null
    private val mModelClass by lazy {
        val parameterizedType = javaClass.genericSuperclass as ParameterizedType
        parameterizedType.actualTypeArguments[0] as Class<VM>
    }

    /**
     * 共享viewModel的key
     */
    open fun getShareViewModelKey(): String? = null

    open fun getViewModelStoreOwner(): ViewModelStoreOwner {
        return ShareViewModelManager.getShareViewOwner(getShareViewModelKey()) ?: this
    }

    open fun getDialogStyleId() = R.style.MyDialogStyle

    open fun canceledOnTouchOutside() = true

    open fun getGravity() = Gravity.CENTER
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = Dialog(activity as FragmentActivity, getDialogStyleId())
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) // 去掉标题栏
        dialog.setCanceledOnTouchOutside(canceledOnTouchOutside()) // 点击外部是否关闭Dialog
        dialog.window?.let {
            it.decorView.setPadding(0, 0, 0, 0)
            it.attributes = it.attributes?.let { attributes ->
                attributes.gravity = getGravity()
                attributes.width = WindowManager.LayoutParams.WRAP_CONTENT
                attributes.height = WindowManager.LayoutParams.WRAP_CONTENT
                attributes
            }
        }
        return dialog
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View? {
        initViewBinding(inflater)
        generateViewModel()
        return mVb.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        listenToViewModelEvents(mVm)
        lifecycle.addObserver(mVm)
        mVb.setVariable(getViewModelId(), mVm)
        mVm.arguments = arguments
        mVm.initData()
    }

    private fun generateViewModel() {
        mVm = ViewModelProvider(getViewModelStoreOwner())[mModelClass]
        ShareViewModelManager.putSharedViewModel(getShareViewModelKey(), this)
    }

    private fun initViewBinding(inflater: LayoutInflater) {
        mVb = DataBindingUtil.inflate(inflater, getLayoutId(), null, false)
        mVb.lifecycleOwner = this
        ViewTreeLifecycleOwner.set(mVb.root, this)
    }

    private fun listenToViewModelEvents(viewModel: VM) {
        viewModel.mStartActivityFlow.listenTo(lifecycle) {
            it.startActivityForResult(this)
        }
        viewModel.mToastContentFlow.listenTo(lifecycle) {
            toast(it)
        }
        viewModel.isClosedSelf.listenTo(lifecycle) {
            if (it == true) {
                dismissAllowingStateLoss()
            }
        }
        viewModel.mHideKeyboard.listenTo(lifecycle) {
            if (it == true) {
                KeyboardUtils.hideSoftInput(dialog?.window)
            }
        }
        viewModel.mLoadingDialogFlow.listenTo(lifecycle) {
            if (TextUtils.isEmpty(it)) {
                mLoadingDialog?.dismissAllowingStateLoss()
                return@listenTo
            }
            if (mLoadingDialog?.dialog?.isShowing == true) {
                return@listenTo
            }
            mLoadingDialog = LoadingDialog()
            mLoadingDialog?.show(childFragmentManager, "LoadingDialog")
        }
    }

    open fun toast(msg: CharSequence?) {
        msg?.let {
            context?.showToast(it)
        }
    }

    @Deprecated("Deprecated in Java")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        mVm.onActivityResult(
            code = requestCode,
            result = data,
            resultCode = resultCode
        )
    }
    override fun show(manager: FragmentManager, tag: String?) {
        showAllowingStateLoss(manager, tag)
    }

    private fun showAllowingStateLoss(manager: FragmentManager, tag: String?) {
        try {
            val dismissed: Field = DialogFragment::class.java.getDeclaredField("mDismissed")
            dismissed.isAccessible = true
            dismissed.set(this, false)
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
        try {
            val shown: Field = DialogFragment::class.java.getDeclaredField("mShownByMe")
            shown.isAccessible = true
            shown.set(this, true)
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
        val ft: FragmentTransaction = manager.beginTransaction()
        ft.add(this, tag)
        ft.commitAllowingStateLoss()
    }
    override fun onDestroyView() {
        super.onDestroyView()
    }

    open fun getViewModelId(): Int = BR.vm

    abstract fun getLayoutId(): Int
}